home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1997 June: System Software / Dev.CD Jun 97 SSW.toast / What's New? / Sample Code / Toolbox / FinderDragPro / FinderDragPro.c < prev    next >
Encoding:
Text File  |  1997-04-23  |  21.4 KB  |  929 lines  |  [TEXT/CWIE]

  1.     //
  2.     //    "FinderDragPro.c"
  3.     //
  4.  
  5. #define OLDROUTINELOCATIONS        0
  6. #define OLDROUTINENAMES            0
  7. #define SystemSevenOrLater        1
  8.  
  9. #include "DragManagerAdditions.h"
  10. #include "Technote.h"
  11. #include "GetIconSuiteFromFinder.h"
  12. #include "FileCopy.h"
  13. #include "DirectoryCopy.h"
  14.  
  15. #ifndef __LOWMEM__
  16. #    include <LowMem.h>
  17. #endif
  18.  
  19. #ifndef __FONTS__
  20. #    include <Fonts.h>
  21. #endif
  22.  
  23. #ifndef __DIALOGS__
  24. #    include <Dialogs.h>
  25. #endif
  26.  
  27. #ifndef __DRAG__
  28. #    include <Drag.h>
  29. #endif
  30.  
  31. #ifndef __ICONS__
  32. #    include <Icons.h>
  33. #endif
  34.  
  35. #ifndef __PLSTRINGFUNCS__
  36. #    include <PLStringFuncs.h>
  37. #endif
  38.  
  39. #ifndef __TEXTUTILS__
  40. #    include <TextUtils.h>
  41. #endif
  42.  
  43. #ifndef __GESTALT__
  44. #    include <Gestalt.h>
  45. #endif
  46.  
  47. #ifndef __QDOFFSCREEN__
  48. #    include <QDOffscreen.h>
  49. #endif
  50.  
  51. static Boolean            gQuitting;                // set when it's time to quit
  52. static FontInfo            gFontInfo;                // cache for Geneva 9 info
  53. static Rect                gIconLimitRect;            // window portRect without text area
  54. static Boolean            gHaveTranslucence;        // can we call SetDragImage?
  55. static FSSpec            gDropLocation;            // cache where user dropped
  56. static HFSFlavor        gHFSFlavor;                // data dragged in, re-used when dragging out
  57. static DragReference    gApprovedDragRef;        // tracking handler approves, receive handler confirms
  58. static ItemReference    gItemRef;                // valid when gApprovedDragRef is being received
  59. static Handle            gIconSuite;                // cached from flavorTypeHFS dragged in
  60. static FSSpec            gDropLocFSS;
  61. static FSSpec            gCopyTarget;
  62. static WindowRef        gWindow;
  63.  
  64. static pascal OSErr InitMac (void)
  65. {
  66.     //
  67.     //    Initialize the toolbox and find out
  68.     //    whether we can call SetDragImage.
  69.     //
  70.  
  71.     OSErr err = noErr;
  72.  
  73.     MaxApplZone ( );
  74.     if (!(err = MemError ( )))
  75.     {
  76.         long gestaltResponse;
  77.  
  78.         InitGraf (&(qd.thePort));
  79.         InitFonts ( );
  80.         InitWindows ( );
  81.         InitMenus ( );
  82.         TEInit ( );
  83.         InitDialogs (nil);
  84.  
  85.         if (!(err = Gestalt (gestaltDragMgrAttr,&gestaltResponse)))
  86.         {
  87.             gHaveTranslucence = !!(gestaltResponse & (1 << gestaltDragMgrHasImageSupport));
  88.         }
  89.     }
  90.  
  91.     return err;
  92. }
  93.  
  94. static pascal void MakeIconRect (Rect *iconRect)
  95. {
  96.     //
  97.     //    Calculate a rectangle sized as a 32x32 icon in the center
  98.     //    of the cached icon limit rect (see gIconLimitRect above).
  99.     //
  100.  
  101.     short    halfLimitHeight        = (gIconLimitRect.bottom - gIconLimitRect.top) / 2,
  102.             halfLimitWidth        = (gIconLimitRect.right - gIconLimitRect.left) / 2;
  103.  
  104.     SetRect (iconRect,0,0,32,32);
  105.     OffsetRect (iconRect, halfLimitWidth - 16, halfLimitHeight - 16);
  106. }
  107.  
  108. static pascal OSErr MakeDragTranslucent
  109.     (DragReference dragRef, const Rect *iconRect, GWorldPtr *imageGWorld, RgnHandle *maskRgn)
  110. {
  111.     //
  112.     //    Add a translucent image of the Finder icon to the DragReference.
  113.     //    Produces a GWorld and a Rgn to be disposed by caller after TrackDrag.
  114.     //
  115.  
  116.     OSErr err = noErr;
  117.  
  118.     *imageGWorld    = nil;
  119.     *maskRgn        = nil;
  120.  
  121.     if (gHaveTranslucence)
  122.     {
  123.         const    short        pixelDepth    = 8;
  124.                 Rect        imageRect    = *iconRect;
  125.                 Point        offsetPt;
  126.  
  127.         SetPt (&offsetPt, imageRect.left, imageRect.top);
  128.         OffsetRect (&imageRect, -(imageRect.left), -(imageRect.top));
  129.         LocalToGlobal (&offsetPt);
  130.  
  131.         err = NewGWorld (imageGWorld, pixelDepth, &imageRect, nil, nil, 0);
  132.         if (err == memFullErr)
  133.             err = NewGWorld (imageGWorld, pixelDepth, &imageRect, nil, nil, useTempMem);
  134.         if (!err)
  135.         {
  136.             *maskRgn = NewRgn ( );
  137.             if (!(err = MemError ( )))
  138.             {
  139.                 if (!(err = IconSuiteToRgn (*maskRgn,&imageRect,kAlignNone,gIconSuite)))
  140.                 {
  141.                     GDHandle        saveDevice;
  142.                     CGrafPtr        savePort;
  143.                     PixMapHandle    imagePixMap        = GetGWorldPixMap (*imageGWorld);
  144.  
  145.                     GetGWorld (&savePort, &saveDevice);
  146.                     (void) LockPixels (imagePixMap);
  147.                     SetGWorld (*imageGWorld, nil);
  148.                     EraseRect (&(qd.thePort->portRect));
  149.                     err = PlotIconSuite (&imageRect,kAlignNone,kTransformNone,gIconSuite);
  150.                     UnlockPixels (imagePixMap);
  151.                     SetGWorld (savePort, saveDevice);
  152.  
  153.                     if (!err)
  154.                         err = SetDragImage (dragRef, imagePixMap, *maskRgn, offsetPt, dragStandardImage);
  155.                 }
  156.             }
  157.         }
  158.  
  159.         if (err)
  160.         {
  161.             if (*imageGWorld)
  162.             {
  163.                 DisposeGWorld (*imageGWorld);
  164.                 *imageGWorld = nil;
  165.             }
  166.  
  167.             if (*maskRgn)
  168.             {
  169.                 DisposeRgn (*maskRgn);
  170.                 *maskRgn = nil;
  171.             }
  172.         }
  173.     }
  174.  
  175.     return err;
  176. }
  177.  
  178. static pascal OSErr RedundantMakeHFSFlavor (HFSFlavor *hfsFlavorP)
  179. {
  180.     //
  181.     //    We could assume that any HFSFlavor we're handed thru
  182.     //    our drag receive handler is valid. However:
  183.     //
  184.     //        [1] It might have become stale since we received it.
  185.     //
  186.     //        [2] We need an excuse to call MakeHFSFlavor so we
  187.     //            can test it before pasting it into the Technote.
  188.     //
  189.     //    Thus, this function confirms/refreshes the HFSFlavor data;
  190.     //    generally we're passed a pointer to the global HFSFlavor cache,
  191.     //    but this code doesn't know it.
  192.     //
  193.  
  194.     OSErr err = noErr;
  195.  
  196.     //
  197.     //    Clear some fields so we can see that MakeHFSFlavor
  198.     //    did its job.
  199.     //
  200.  
  201.     hfsFlavorP->fileType        =
  202.     hfsFlavorP->fileCreator        =
  203.     hfsFlavorP->fdFlags            = 0;
  204.  
  205.     err = MakeHFSFlavor (    hfsFlavorP->fileSpec.vRefNum,
  206.                             hfsFlavorP->fileSpec.parID,
  207.                             hfsFlavorP->fileSpec.name,
  208.                             hfsFlavorP                        );
  209.  
  210.     return err;
  211. }
  212.  
  213. static pascal OSErr WakeUpCurrentProcess (void)
  214. {
  215.     ProcessSerialNumber curPSN = { 0, kCurrentProcess };
  216.     return WakeUpProcess (&curPSN); // "post" a null event
  217. }
  218.  
  219. static pascal OSErr CopyDroppedFileOrFolder
  220.     (DragReference dragRef, ItemReference itemRef, const PromiseHFSFlavor *phfs)
  221. {
  222.     //
  223.     //    Figure out where the user dropped the file or folder and create a dummy.
  224.     //    Then prepare to send an AppleEvent to Finder to overwrite the dummy.
  225.     //    Don't send the AppleEvent yet because we need to delete the dummy file
  226.     //    after TrackDrag completes so that Drag Manager doesn't display rejection
  227.     //    feedback to the user. Look at the code surrounding
  228.     //    AddTranslucentIconAndDrag's call to TrackDrag for more info.
  229.     //
  230.  
  231.     OSErr err = noErr;
  232.  
  233.     if (!(err = GetDropDirectory (dragRef,&gDropLocFSS)))
  234.     {
  235.         long dropDirID;
  236.  
  237.         if (!(err = GetDirID (&gDropLocFSS,&dropDirID)))
  238.             if (!(err = FSMakeFSSpec (gDropLocFSS.vRefNum, dropDirID, gHFSFlavor.fileSpec.name, &gCopyTarget)))
  239.                 err = dupFNErr;
  240.             else if (err == fnfErr)
  241.                 if (!(err = CreatePromisedFileOrFolder (phfs,&gCopyTarget,smSystemScript)))
  242.                     err = SetPromisedHFSFlavorData (dragRef,itemRef,phfs,&gCopyTarget);
  243.  
  244.         if (err)
  245.         {
  246.             gDropLocFSS.vRefNum        = 0;
  247.             gCopyTarget.vRefNum        = 0;
  248.         }
  249.     }
  250.  
  251.     return err;
  252. }
  253.  
  254. static pascal OSErr MyDragSendDataProc
  255.     (FlavorType flavorType, void *, ItemReference itemRef, DragReference dragRef)
  256. {
  257.     //
  258.     //    This function fulfills the promise of the promisedFlavor data.
  259.     //    See comments in CopyDroppedFileOrFolder.
  260.     //
  261.  
  262.     OSErr err = noErr;
  263.  
  264.     PromiseHFSFlavor  phfs;
  265.     Size              size = sizeof (phfs);
  266.  
  267.     if (!(err = GetFlavorData (dragRef,itemRef,flavorTypePromiseHFS,&phfs,&size,0)))
  268.     {
  269.         if (size != sizeof (phfs))
  270.             err = cantGetFlavorErr;
  271.         else if (flavorType == phfs.promisedFlavor)
  272.         {
  273.             Boolean shouldCopy;
  274.  
  275.             if (!(err = ShouldCopyToDropLoc (dragRef,flavorType,&shouldCopy)))
  276.             {
  277.                 if (shouldCopy)
  278.                 {
  279.                     SetCursor (*GetCursor (watchCursor));
  280.                     err = CopyDroppedFileOrFolder (dragRef,itemRef,&phfs);
  281.                 }
  282.                 else
  283.                 {
  284.                     err = SetPromisedHFSFlavorData
  285.                         (dragRef,itemRef,&phfs,&(gHFSFlavor.fileSpec));
  286.                 }
  287.             }
  288.         }
  289.     }
  290.  
  291.     return err;
  292. }
  293.  
  294. static pascal OSErr AttachSendDataProc (DragReference dragRef)
  295. {
  296.     //
  297.     //    Cretaes a UPP for the SendDataProc if necessary
  298.     //    and attaches it to the given DragReference.
  299.     //
  300.  
  301.     OSErr err = noErr;
  302.  
  303.     static DragSendDataProc dragSendDataProc;
  304.  
  305.     if (!dragSendDataProc)
  306.         dragSendDataProc = NewDragSendDataProc (MyDragSendDataProc);
  307.     if (!dragSendDataProc)
  308.         err = nilHandleErr;
  309.     else
  310.         err = SetDragSendProc (dragRef,dragSendDataProc,nil);
  311.  
  312.     return err;
  313. }
  314.  
  315. static pascal OSErr AddTranslucentIconAndDrag
  316.     (DragReference dragRef, const EventRecord *event, const Rect *iconRect)
  317. {
  318.     //
  319.     //    Add an appropriate icon to the DragReference. Drag.
  320.     //    Copy any necessary files as result from the drag.
  321.     //
  322.  
  323.     OSErr err = noErr;
  324.  
  325.     RgnHandle dragRgn = NewRgn ( );
  326.     if (!(err = MemError ( )))
  327.     {
  328.         if (!(err = IconSuiteToRgn (dragRgn,iconRect,kAlignNone,gIconSuite)))
  329.         {
  330.             RgnHandle insetRgn = NewRgn ( );
  331.             if (!(err = MemError ( )))
  332.             {
  333.                 Point        globalOrigin;
  334.                 GWorldPtr    imageGWorld;
  335.                 RgnHandle    maskRgn;
  336.  
  337.                 CopyRgn (dragRgn,insetRgn);
  338.                 InsetRgn (insetRgn,1,1);
  339.                 DiffRgn (dragRgn,insetRgn,dragRgn);
  340.                 SetPt (&globalOrigin,qd.thePort->portRect.left,qd.thePort->portRect.top);
  341.                 LocalToGlobal (&globalOrigin);
  342.                 OffsetRgn (dragRgn,globalOrigin.h,globalOrigin.v);
  343.  
  344.                 (void) MakeDragTranslucent (dragRef,iconRect,&imageGWorld,&maskRgn);
  345.                 // ignore errors; we don't care very much; dispose whatever we get after
  346.  
  347.                 err = TrackDrag (dragRef,event,dragRgn);
  348.  
  349.                 DisposeRgn (insetRgn);
  350.                 if (!err) err = MemError ( );
  351.  
  352.                 if (imageGWorld)
  353.                     DisposeGWorld (imageGWorld);
  354.                 if (maskRgn)
  355.                     DisposeRgn (maskRgn);
  356.             }
  357.         }
  358.         DisposeRgn (dragRgn);
  359.         if (!err) err = MemError ( );
  360.     }
  361.  
  362.     return err;
  363. }
  364.  
  365. static pascal OSErr DragOut (const EventRecord *event, const Rect *iconRect)
  366. {
  367.     //
  368.     //    Administrates the process of dragging the icon out of the window.
  369.     //    If the option key is being held down, use flavorTypePromiseHFS
  370.     //    so drag receivers can ask for a copy. Make the drag translucent.
  371.     //    Copy the file via MoreFiles if appropriate.
  372.     //
  373.  
  374.     OSErr err = noErr;
  375.  
  376.     if (WaitMouseMoved (event->where))
  377.     {
  378.         DragReference dragRef;
  379.  
  380.         if (!(err = NewDrag (&dragRef)))
  381.         {
  382.             OSErr err2 = noErr;
  383.  
  384.             if (!(event->modifiers & optionKey))
  385.             {
  386.                 if (!(err = RedundantMakeHFSFlavor (&gHFSFlavor)))
  387.                     err = AddDragItemFlavor
  388.                         (dragRef, 0, flavorTypeHFS, &gHFSFlavor, sizeof (gHFSFlavor), 0);
  389.             }
  390.             else if (!(err = AttachSendDataProc (dragRef)))
  391.             {
  392.                 FlavorType promisedFlavor =
  393.                     (event->modifiers & cmdKey) ? kPromisedFlavorFindFile : kPromisedFlavor;
  394.                 err = AddDragItemFlavorTypePromiseHFS
  395.                     (dragRef,0,gHFSFlavor.fileType,gHFSFlavor.fileCreator,
  396.                         gHFSFlavor.fdFlags,promisedFlavor);
  397.             }
  398.  
  399.             if (!err)
  400.             {
  401.                 gCopyTarget.vRefNum        = 0;
  402.                 gDropLocFSS.vRefNum        = 0;
  403.  
  404.                 if (!(err = AddTranslucentIconAndDrag (dragRef,event,iconRect)))
  405.                 {
  406.                     if (!err && gDropLocFSS.vRefNum && gCopyTarget.vRefNum)
  407.                     {
  408.                         if (!(err = FSpDelete (&gCopyTarget)))
  409.                         {
  410.                             if (gHFSFlavor.fileType == 'fold' || gHFSFlavor.fileType == 'disk')
  411.                                 err = FSpDirectoryCopy (&(gHFSFlavor.fileSpec),&gDropLocFSS,nil,0,true,nil);
  412.                             else
  413.                                 err = FSpFileCopy (&(gHFSFlavor.fileSpec),&gDropLocFSS,nil,nil,0,true);
  414.                         }
  415.                     }
  416.                 }
  417.             }
  418.  
  419.             err2 = DisposeDrag (dragRef);
  420.             if (!err) err = err2;
  421.         }
  422.     }
  423.  
  424.     return err;
  425. }
  426.  
  427. static pascal OSErr ClickInContent (WindowRef whichWindow, const EventRecord *event)
  428. {
  429.     //
  430.     //    See if the click was in the icon. If so, highlight the icon and
  431.     //    let someone else decide whether to drag.
  432.     //
  433.  
  434.     OSErr err = noErr;
  435.  
  436.     if (gIconSuite)
  437.     {
  438.         Rect        iconRect;
  439.         Point        localPoint    = event->where;
  440.         GrafPtr        savedPort    = qd.thePort;
  441.  
  442.         SetPort (whichWindow);
  443.  
  444.         GlobalToLocal (&localPoint);
  445.         MakeIconRect (&iconRect);
  446.  
  447.         if (PtInIconSuite (localPoint,&iconRect,kAlignNone,gIconSuite))
  448.         if (!(err = PlotIconSuite (&iconRect,kAlignNone,kTransformSelected,gIconSuite)))
  449.         {
  450.             OSErr err2 = noErr;
  451.             err = DragOut (event,&iconRect);
  452.             err2 = PlotIconSuite (&iconRect,kAlignNone,kTransformNone,gIconSuite);
  453.             if (!err) err = err2;
  454.         }
  455.  
  456.         SetPort (savedPort);
  457.     }
  458.  
  459.     return err;
  460. }
  461.  
  462. static pascal OSErr MouseDown (const EventRecord *event)
  463. {
  464.     OSErr err = noErr;
  465.  
  466.     WindowRef whichWindow;
  467.     short partCode = FindWindow (event->where,&whichWindow);
  468.  
  469.     Rect boundsRect;
  470.  
  471.     switch (partCode)
  472.     {
  473.         case inContent :
  474.  
  475.             if (whichWindow != FrontWindow ( ))
  476.                 SelectWindow (whichWindow);
  477.             else
  478.                 err = ClickInContent (whichWindow,event);
  479.             break;
  480.  
  481.         case inGoAway :
  482.  
  483.             if (TrackGoAway (whichWindow,event->where))
  484.                 gQuitting = true;
  485.             break;
  486.  
  487.         case inDrag :
  488.  
  489.             boundsRect = qd.screenBits.bounds;
  490.             InsetRect (&boundsRect,4,4);
  491.             DragWindow (whichWindow,event->where,&boundsRect);
  492.             break;
  493.     }
  494.  
  495.     return err;
  496. }
  497.  
  498. static pascal void DrawCenteredString (ConstStr255Param str, short baseLine)
  499. {
  500.     Rect    *portRect        = &(qd.thePort->portRect);
  501.     short    portWidth        = portRect->right - portRect->left,
  502.             stringWidth        = StringWidth (str);
  503.  
  504.     MoveTo (portRect->left + (portWidth / 2) - (stringWidth / 2), baseLine);
  505.     DrawString (str);
  506. }
  507.  
  508. static pascal void AppendOSTypeToString (OSType ost, StringPtr str)
  509. {
  510.     BlockMoveData (&ost,str+*str+1,sizeof(ost));
  511.     *str += sizeof(ost);
  512. }
  513.  
  514. static pascal void AppendHex16ToString (UInt16 ui, StringPtr str)
  515. {
  516.     unsigned char *scan = str + *str + 1;
  517.     unsigned char nybbleIndex = 0;
  518.     static const char hexDigits [ ] = "0123456789ABCDEF";
  519.  
  520.     do
  521.         scan [3 - nybbleIndex] = hexDigits [(ui >> (4 * nybbleIndex)) & 0x0F];
  522.     while (++nybbleIndex < 4);
  523.  
  524.     *str += 4;
  525. }
  526.  
  527. static pascal OSErr UpdateWindow (WindowRef whichWindow)
  528. {
  529.     OSErr err = noErr;
  530.  
  531.     GrafPtr savedPort = qd.thePort;
  532.  
  533.     SetPort (whichWindow);
  534.     BeginUpdate (whichWindow);
  535.  
  536.     EraseRect (&(qd.thePort->portRect));
  537.  
  538.     if (gIconSuite)
  539.     {
  540.         Rect iconRect;
  541.  
  542.         MakeIconRect (&iconRect);
  543.  
  544.         err = PlotIconSuite (&iconRect,kAlignNone,kTransformNone,gIconSuite);
  545.  
  546.         if (err == noMaskFoundErr)
  547.             err = noErr;
  548.  
  549.         if (!err)
  550.         {
  551.             StringPtr str = nil;
  552.             short baseLine = gIconLimitRect.bottom + gFontInfo.ascent;
  553.  
  554.             DrawCenteredString (gHFSFlavor.fileSpec.name, baseLine);
  555.  
  556.             str = (StringPtr) NewPtr (sizeof (Str255));
  557.             if (!(err = MemError ( )))
  558.             {
  559.                 Str15 scratch;
  560.  
  561.                 PLstrcpy (str,"\pvRefNum ");
  562.                 NumToString (gHFSFlavor.fileSpec.vRefNum, scratch);
  563.                 PLstrcat (str,scratch);
  564.                 PLstrcat (str,"\p, parID ");
  565.                 NumToString (gHFSFlavor.fileSpec.parID, scratch);
  566.                 PLstrcat (str,scratch);
  567.                 baseLine += gFontInfo.ascent + gFontInfo.leading;
  568.                 DrawCenteredString (str, baseLine);
  569.  
  570.                 *str = 0;
  571.                 AppendOSTypeToString (gHFSFlavor.fileType, str);
  572.                 PLstrcat (str,"\p ");
  573.                 AppendOSTypeToString (gHFSFlavor.fileCreator, str);
  574.                 PLstrcat (str,"\p 0x");
  575.                 AppendHex16ToString (gHFSFlavor.fdFlags, str);
  576.                 baseLine += gFontInfo.ascent + gFontInfo.leading;
  577.                 DrawCenteredString (str, baseLine);
  578.  
  579.                 DisposePtr ((Ptr) str);
  580.                 if (!err) err = MemError ( );
  581.             }
  582.         }
  583.     }
  584.  
  585.     EndUpdate (whichWindow);
  586.     SetPort (savedPort);
  587.  
  588.     return err;
  589. }
  590.  
  591. static pascal void InvalWindow (WindowRef wRef)
  592. {
  593.     //    "post" an update event for the entire window
  594.  
  595.     GrafPtr savedPort = qd.thePort;
  596.     SetPort (wRef);
  597.     InvalRect (&(qd.thePort->portRect));
  598.     SetPort (savedPort);
  599. }
  600.  
  601. static pascal OSErr RebuildIcon (WindowRef wRef)
  602. {
  603.     OSErr err = noErr;
  604.  
  605.     if (!gIconSuite && gHFSFlavor.fileSpec.vRefNum)
  606.         if (!(err = GetIconSuiteFromFinder (&(gHFSFlavor.fileSpec),&gIconSuite)))
  607.             InvalWindow (wRef);
  608.  
  609.     return err;
  610. }
  611.  
  612. static pascal OSErr NullEvent (void)
  613. {
  614.     OSErr err = noErr;
  615.  
  616.     if (gWindow)
  617.         if (!(err = RebuildIcon (gWindow)))
  618.             ;
  619.  
  620.     return err;
  621. }
  622.  
  623. static pascal OSErr OneEvent (const EventRecord *event)
  624. {
  625.     OSErr err = noErr;
  626.  
  627.     switch (event->what)
  628.     {
  629.         case kHighLevelEvent :
  630.  
  631.             err = AEProcessAppleEvent (event);
  632.             break;
  633.  
  634.         case mouseDown :
  635.  
  636.             err = MouseDown (event);
  637.             break;
  638.  
  639.         case updateEvt :
  640.  
  641.             err = UpdateWindow ((WindowRef) (event->message));
  642.             break;
  643.  
  644.         case nullEvent :
  645.  
  646.             err = NullEvent ( );
  647.             break;
  648.     }
  649.  
  650.     return err;
  651. }
  652.  
  653. static pascal void LoopEvents (void)
  654. {
  655.     do
  656.     {
  657.         OSErr err;
  658.  
  659.         EventRecord event;
  660.         WaitNextEvent (everyEvent,&event,-1,nil);
  661.         err = OneEvent (&event);
  662.         if (err) break;
  663.         InitCursor ( );
  664.     }
  665.     while (!gQuitting);
  666. }
  667.  
  668. static pascal OSErr ShowDragHiliteWindow (DragReference dragRef, WindowRef wRef)
  669. {
  670.     OSErr err = noErr;
  671.  
  672.     RgnHandle hiliteRgn = NewRgn ( );
  673.     if (!hiliteRgn)
  674.         err = nilHandleErr;
  675.     else
  676.     {
  677.         RectRgn (hiliteRgn,&(wRef->portRect));
  678.         err = ShowDragHilite (dragRef,hiliteRgn,true);
  679.         DisposeRgn (hiliteRgn);
  680.     }
  681.  
  682.     return err;
  683. }
  684.  
  685. static pascal OSErr ApproveDragReference (DragReference theDragRef, Boolean *approved)
  686. {
  687.     //
  688.     //    We accept one item and one item only.
  689.     //    It must have either 'flavorTypeHFS' or 'flavorTypePromiseHFS'.
  690.     //    If the user held the option key down when beginning the drag
  691.     //    or is holding the option key down when the drag enters, we
  692.     //    accept only 'flavorTypePromiseHFS'.
  693.     //
  694.     //    Note that if a flavor can't be found, it's not really an
  695.     //    error; it only means the flavor wasn't there and we should
  696.     //    not accept the drag. Therefore, we translate 'badDragFlavorErr'
  697.     //    into a 'false' value for '*approved'.
  698.     //
  699.  
  700.     OSErr err = noErr;
  701.  
  702.     UInt16            itemCount;
  703.     DragAttributes    dragAttrs;
  704.     short            currentModifers,
  705.                     mouseDownModifiers;
  706.  
  707.     *approved = false;
  708.  
  709.     if (!(err = GetDragAttributes (theDragRef,&dragAttrs)))
  710.     if (!(dragAttrs & dragInsideSenderWindow))
  711.     if (!(err = CountDragItems (theDragRef,&itemCount)) && itemCount == 1)
  712.     if (!(err = GetDragItemReferenceNumber (theDragRef,1,&gItemRef)))
  713.     if (!(err = GetDragModifiers (theDragRef,¤tModifers,&mouseDownModifiers,nil)))
  714.     {
  715.         if ((currentModifers & optionKey) || (mouseDownModifiers & optionKey))
  716.             err = badDragFlavorErr; // pretend to have searched and not found
  717.         else
  718.         {
  719.             FlavorFlags flavorFlags;
  720.             err = GetFlavorFlags (theDragRef,gItemRef,flavorTypeHFS,&flavorFlags);
  721.         }
  722.  
  723.         if (!err)
  724.             *approved = true;
  725.         else if (err == badDragFlavorErr)
  726.         {
  727.             PromiseHFSFlavor    promiseHFSFlavor;
  728.             Size                dataSize;
  729.  
  730.             dataSize = sizeof (promiseHFSFlavor);
  731.             err = GetFlavorData (theDragRef,gItemRef,flavorTypePromiseHFS,&promiseHFSFlavor,&dataSize,0);
  732.  
  733.             if (err == badDragFlavorErr)
  734.                 err = noErr; // *approved = false;
  735.             else if (!err)
  736.             {
  737.                 if (dataSize != sizeof (promiseHFSFlavor))
  738.                     err = cantGetFlavorErr;
  739.                 else
  740.                     *approved = true;
  741.             }
  742.         }
  743.     }
  744.  
  745.     return err;
  746. }
  747.  
  748. static pascal OSErr MyDragTrackingHandler
  749.     (DragTrackingMessage message, WindowPtr theWindow, void *, DragReference theDragRef)
  750. {
  751.     Boolean approved;
  752.  
  753.     switch (message)
  754.     {
  755.         case dragTrackingEnterWindow    :
  756.  
  757.             if (!ApproveDragReference (theDragRef,&approved) && approved)
  758.             if (!ShowDragHiliteWindow (theDragRef,theWindow))
  759.                 gApprovedDragRef = theDragRef;
  760.             break;
  761.  
  762.         case dragTrackingInWindow        :
  763.  
  764.             // do nothing
  765.             break;
  766.  
  767.         case dragTrackingLeaveWindow    :
  768.  
  769.             (void) HideDragHilite (theDragRef);
  770.             // fall thru
  771.  
  772.         default :
  773.  
  774.             gApprovedDragRef = nil;
  775.             break;
  776.     }
  777.  
  778.     return noErr; // there's no point in confusing Drag Manager or its caller
  779. }
  780.  
  781. static pascal OSErr MyDragReceiveHandler (WindowPtr, void *, DragReference theDragRef)
  782. {
  783.     //
  784.     //    Receive a drag.
  785.     //
  786.     //    First attempt to grab flavorTypePromiseHFS data
  787.     //    without specifying a drop location to induce Find File to
  788.     //    reveal its secrets. If we're not dealing
  789.     //    with Find File, make sure there's a folder to receive
  790.     //    the new file and and receive the file there.
  791.     //
  792.     //    Once the file's been received, we need a null event sent
  793.     //    to our event loop because that's when we check to see if
  794.     //    gHFSFlavor has changed. Since Drag Manager callbacks run
  795.     //    outside the event loop, we might otherwise only receive
  796.     //    a null event after quite a long time, especially if we
  797.     //    specify a long sleep time when calling WaitNextEvent.
  798.     //    (See RebuildIcon and its caller.)
  799.     //
  800.  
  801.     OSErr err = dragNotAcceptedErr;
  802.  
  803.     if (theDragRef == gApprovedDragRef)
  804.     {
  805.         err = GetHFSFlavorFromDragReference (gApprovedDragRef,gItemRef,&gHFSFlavor);
  806.  
  807.         if (err == badDragFlavorErr)
  808.         {
  809.             err = ReceivePromisedFile (gApprovedDragRef,gItemRef,&gHFSFlavor,nil);
  810.  
  811.             if (err == paramErr) // it's not from Find File
  812.             {
  813.                 if (*(gDropLocation.name))
  814.                     err = noErr;
  815.                 else
  816.                 {
  817.                     // real programs don't use string constants
  818.                     err = FSMakeFSSpec (0,0,"\pDropSpool",&gDropLocation);
  819.                     if (err == fnfErr)
  820.                     {
  821.                         long createdDirID;
  822.                         // specify smRoman because of the string constant
  823.                         err = FSpDirCreate (&gDropLocation,smRoman,&createdDirID);
  824.                     }
  825.                 }
  826.  
  827.                 if (!err)
  828.                     err = ReceivePromisedFile
  829.                         (gApprovedDragRef,gItemRef,&gHFSFlavor,&gDropLocation);
  830.             }
  831.         }
  832.  
  833.         if (!err)
  834.         {
  835.             if (!(err = WakeUpCurrentProcess ( )))
  836.             {
  837.                 if (gIconSuite)
  838.                 {
  839.                     DisposeIconSuite (gIconSuite,true);
  840.                     gIconSuite = nil;
  841.                 }
  842.             }
  843.         }
  844.     }
  845.  
  846.     return err;
  847. }
  848.  
  849. static pascal OSErr CreateFinderDragProWindow (void)
  850. {
  851.     OSErr err = noErr;
  852.  
  853.     DragReceiveHandlerUPP    dragReceiveHandlerUPP    = nil;
  854.     DragTrackingHandlerUPP    dragTrackingHandlerUPP    = nil;
  855.     Rect                    boundsRect;
  856.     unsigned short            top;
  857.  
  858.     gWindow = nil;
  859.  
  860.     top = qd.screenBits.bounds.top + GetMBarHeight ( ) + 20;
  861.     SetRect (&boundsRect, 20, top + 50, 200, top + 150);
  862.  
  863.     gWindow = NewCWindow
  864.         (nil, &boundsRect, LMGetCurApName ( ), true, noGrowDocProc, (WindowRef) -1, true, 0);
  865.  
  866.     if (!gWindow)
  867.         err = nilHandleErr;
  868.     else
  869.     {
  870.         dragTrackingHandlerUPP = NewDragTrackingHandlerProc (MyDragTrackingHandler);
  871.  
  872.         if (!dragTrackingHandlerUPP)
  873.             err = nilHandleErr;
  874.         else
  875.         {
  876.             dragReceiveHandlerUPP = NewDragReceiveHandlerProc (MyDragReceiveHandler);
  877.  
  878.             if (!dragReceiveHandlerUPP)
  879.                 err = nilHandleErr;
  880.             else if (!(err = InstallTrackingHandler (dragTrackingHandlerUPP,gWindow,nil)))
  881.             {
  882.                 err = InstallReceiveHandler (dragReceiveHandlerUPP,gWindow,nil);
  883.                 if (err)
  884.                     RemoveTrackingHandler (dragTrackingHandlerUPP,gWindow);
  885.                 else
  886.                 {
  887.                     GrafPtr savedPort = qd.thePort;
  888.                     SetPort (gWindow);
  889.                     TextSize (9);
  890.                     GetFontInfo (&gFontInfo);
  891.                     gIconLimitRect = qd.thePort->portRect;
  892.                     gIconLimitRect.bottom -= 3 * (gFontInfo.ascent + gFontInfo.leading + gFontInfo.descent);
  893.                     SetPort (savedPort);
  894.                 }
  895.             }
  896.         }
  897.     }
  898.  
  899.     if (err)
  900.     {
  901.         if (gWindow)                    DisposeWindow (gWindow);
  902.         if (dragTrackingHandlerUPP)        DisposeRoutineDescriptor (dragTrackingHandlerUPP);
  903.         if (dragReceiveHandlerUPP)        DisposeRoutineDescriptor (dragReceiveHandlerUPP);
  904.  
  905.         gWindow = nil;
  906.     }
  907.  
  908.     return err;
  909. }
  910.  
  911. static pascal OSErr NewApplicationAppleEventHandler (const AppleEvent *, AppleEvent *, long)
  912. {
  913.     (void) CreateFinderDragProWindow ( );
  914.     return noErr;
  915. }
  916.  
  917. void main (void)
  918. {
  919.     if (InitMac ( ))
  920.         SysBeep (10);
  921.     else
  922.     {
  923.         (void) InstallBogusFinderEventHandler ( );
  924.         AEInstallEventHandler (kCoreEventClass,kAEOpenApplication,
  925.             NewAEEventHandlerProc (NewApplicationAppleEventHandler),0,false);
  926.         LoopEvents ( );
  927.     }
  928. }
  929.